﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace RobotMapper
{
    internal class RobotMapperConfig<Source, Target> : IRobotMapperConfig<Source, Target>
    {
        //是否为局部配置默认为否
        private bool _IsPrat = false;

        public RobotMapperConfig(bool isPrat = false)
        {
            if (isPrat) _IsPrat = true;
        }

        #region Bind

        /// <summary>
        /// 将源属性与目标属性绑定 可按照自定义规则
        /// </summary>
        /// <param name="source">源属性绑定规则</param>
        /// <param name="target">目标属性</param>
        public void Bind(Func<Source, object> source, Expression<Func<Target, object>> target)
        {
            //获取目标属性名称
            var tProName = RobotMapperHelper.GetPropertyName(target);

            var bindFuncRoles = _IsPrat ? PartConfig.BindFuncRoles : GlobalConfig.BindFuncRoles;

            //重复配置则覆盖
            lock (GlobalConfig.BindFuncRoleLock)
            {
                var bindRole = bindFuncRoles.SingleOrDefault(a =>
                 a.SourceType == typeof(Source) &&
                 a.TargetType == typeof(Target) &&
                 a.TargetPropertyName == tProName);
                if (bindRole == null)
                    bindFuncRoles.Add(new BindFuncRole()
                    {
                        SourceType = typeof(Source),
                        TargetType = typeof(Target),
                        SourceFunc = (a) =>
                        {
                            return source?.Invoke((Source)a);
                        },
                        TargetPropertyName = tProName
                    });
                else
                    bindRole.SourceFunc = (a) =>
                    {
                        return source?.Invoke((Source)a);
                    };
            }
        }

        /// <summary>
        /// 将源属性与目标属性绑定 仅仅指定属性名称绑定
        /// </summary>
        /// <param name="source">源属性</param>
        /// <param name="target">目标属性</param>
        public void BindName(Expression<Func<Source, object>> source, Expression<Func<Target, object>> target)
        {
            //获取源属性名称
            var sProName = RobotMapperHelper.GetPropertyName(source);

            //获取目标属性名称
            var tProName = RobotMapperHelper.GetPropertyName(target);

            var bindPropertyRoles = _IsPrat ? PartConfig.BindPropertyRoles : GlobalConfig.BindPropertyRoles;

            //若无配置则加配置
            lock (GlobalConfig.BindPropertyRoleLock)
            {
                var bindRole = bindPropertyRoles.SingleOrDefault(a =>
                 a.SourceType == typeof(Source) &&
                 a.TargetType == typeof(Target) &&
                 a.SourcePropertyName == sProName &&
                 a.TargetPropertyName == tProName);
                if (bindRole == null)
                    bindPropertyRoles.Add(new BindPropertyRole()
                    {
                        SourceType = typeof(Source),
                        TargetType = typeof(Target),
                        SourcePropertyName = sProName,
                        TargetPropertyName = tProName,
                    });
            }
        }

        /// <summary>
        /// 将源属性与目标属性绑定 仅仅指定属性名称绑定
        /// </summary>
        /// <param name="source">源属性</param>
        /// <param name="target">目标属性</param>
        public void BindName(string sProName, string tProName)
        {
            var bindPropertyRoles = _IsPrat ? PartConfig.BindPropertyRoles : GlobalConfig.BindPropertyRoles;

            //若无配置则加配置
            lock (GlobalConfig.BindPropertyRoleLock)
            {
                var bindRole = bindPropertyRoles.SingleOrDefault(a =>
                 a.SourceType == typeof(Source) &&
                 a.TargetType == typeof(Target) &&
                 a.SourcePropertyName == sProName &&
                 a.TargetPropertyName == tProName);
                if (bindRole == null)
                    bindPropertyRoles.Add(new BindPropertyRole()
                    {
                        SourceType = typeof(Source),
                        TargetType = typeof(Target),
                        SourcePropertyName = sProName,
                        TargetPropertyName = tProName,
                    });
            }
        }

        #endregion

        #region Ignore

        /// <summary>
        /// 添加目标的忽略属性 单个添加
        /// </summary>
        /// <param name="targetIgnore">目标属性</param>
        public void Ignore(Expression<Func<Target, object>> targetIgnore)
        {
            var ignoreRoles = _IsPrat ? PartConfig.IgnoreRoles : GlobalConfig.IgnoreRoles;

            //获取目标属性名称
            lock (GlobalConfig.IgnoreRoleLock)
            {
                var tProName = RobotMapperHelper.GetPropertyName(targetIgnore);
                var role = ignoreRoles.SingleOrDefault(a =>
                a.SourceType == typeof(Source) &&
                a.TargetType == typeof(Target));
                if (role == null)
                {
                    var newRole = new IgnoreRole()
                    {
                        SourceType = typeof(Source),
                        TargetType = typeof(Target),
                    };
                    newRole.IgnoreMembers.Add(tProName);
                    ignoreRoles.Add(newRole);
                }
                else
                {
                    if (!role.IgnoreMembers.Contains(tProName))
                        role.IgnoreMembers.Add(tProName);
                }
            }
        }

        /// <summary>
        /// 添加目标的忽略属性 批量添加
        /// </summary>
        /// <param name="ignoreProperty">目标属性</param>
        public void Ignore(params string[] ignoreProperty)
        {
            var ignoreRoles = _IsPrat ? PartConfig.IgnoreRoles : GlobalConfig.IgnoreRoles;

            //获取目标属性名称
            lock (GlobalConfig.IgnoreRoleLock)
            {
                var role = ignoreRoles.SingleOrDefault(a => a.SourceType == typeof(Source) && a.TargetType == typeof(Target));
                if (role == null)
                {
                    var newRole = new IgnoreRole()
                    {
                        SourceType = typeof(Source),
                        TargetType = typeof(Target),
                    };
                    newRole.IgnoreMembers.AddRange(ignoreProperty.ToList());
                }
                else
                {
                    var newMembers = ignoreProperty.Except(role.IgnoreMembers).ToList();
                    role.IgnoreMembers.AddRange(newMembers);
                }
            }
        }

        #endregion
    }
}
